﻿#pragma once

#include "common.h"
#include "texture.h"
#include <exception>

namespace anip
{
	class Shader;
	class Context;
	class RenderBuffer;

	class Effect
	{
	public:
		struct ParameterInfo
		{
			const int nFloat, nInt, nBool, nCol, nVec, nTex;
			const string *floatNames, *intNames, *boolNames, *colNames, *vecNames, *texNames;
		};

	protected:
		template<typename T>
		struct Parameter
		{
			bool mapped = false;
			T value;
			const Texture2D* map = nullptr;
		};

		struct TextureParameter
		{
			const Texture2D* value = nullptr;
			int w = 0, h = 0;
		};

		const char* basicVert = 
			"attribute vec2 vPos;"
			"in vec2 pos;"
			"void main()"
			"{"
				"gl_Position = vec4(vPos, 0.0, 1.0);"
				"pos = vPos / 2.0 + vec2(.5, .5);"
			"}";

		Parameter<float> *floatParams;
		Parameter<int> *intParams;
		Parameter<bool> *boolParams;
		Parameter<color> *colorParams;
		Parameter<vec2> *vecParams;
		TextureParameter *texParams;

		float* floatOutputs;
		int* intOutputs;
		bool* boolOutputs;
		color* colorOutputs;
		vec2* vecOutputs;
		TextureParameter *texOutputs;

	public:
		virtual const string name() const = 0;
		virtual const ParameterInfo& inputs() = 0;
		virtual const ParameterInfo& outputs() = 0;
		
		void setInt(int id, int value = 0)
		{
			intParams[id] = { false, value, nullptr };
		}

		void setIntMapped(int id, const Texture2D* value = nullptr)
		{
			intParams[id] = { true, 0, value };
		}

		void setFloat(int id, float value = 0)
		{
			floatParams[id] = { false, value, nullptr };
		}

		void setFloatMapped(int id, const Texture2D* value = nullptr)
		{
			floatParams[id] = { true, 0, value };
		}

		void setBool(int id, bool value)
		{
			boolParams[id] = { false, value, nullptr };
		}

		void setBoolMapped(int id, const Texture2D* value = nullptr)
		{
			boolParams[id] = { true, 0, value };
		}

		void setColor(int id, color value)
		{
			colorParams[id] = { false, value, nullptr };
		}

		void setColorMapped(int id, const Texture2D* value = nullptr)
		{
			colorParams[id] = { true, {0, 0, 0, 0}, value };
		}

		void setVec2(int id, vec2 value)
		{
			vecParams[id] = { false, value, nullptr };
		}

		void setVec2Mapped(int id, const Texture2D* value = nullptr)
		{
			vecParams[id] = { true, {0, 0}, value };
		}

		void setTexture(int id, const Texture2D& value)
		{
			texParams[id] = { &value, value.width(), value.height() };
		}

		void setTextureNull(int id, int w = -1, int h = -1)
		{
			texParams[id] = { nullptr, w, h };
		}

		int getInt(int id) const
		{
			assert(!intParams[id].mapped);
			return intParams[id].value;
		}

		const Texture2D *getIntMapped(int id) const
		{
			assert(intParams[id].mapped);
			return intParams[id].map;
		}

		float getFloat(int id) const
		{
			assert(!floatParams[id].mapped);
			return floatParams[id].value;
		}

		const Texture2D *getFloatMapped(int id) const
		{
			assert(floatParams[id].mapped);
			return floatParams[id].map;
		}

		bool getBool(int id) const
		{
			assert(!boolParams[id].mapped);
			return boolParams[id].value;
		}

		const Texture2D *getBoolMapped(int id) const
		{
			assert(boolParams[id].mapped);
			return boolParams[id].map;
		}

		const TextureParameter& getTexture(int id) const
		{
			return texParams[id];
		}

		float getFloatOutput(int id) const
		{
			return floatOutputs[id];
		}

		int getOutputInt(int id) const
		{
			return intOutputs[id];
		}

		bool getOutputBool(int id) const
		{
			return boolOutputs[id];
		}

		void getOutputColor(int id, color& out) const
		{
			out = colorOutputs[id];
		}

		void getOutputVec2(int id, vec2& out) const
		{
			out = vecOutputs[id];
		}

		const TextureParameter& getOutputTexture(int id) const
		{
			return texOutputs[id];
		}

		virtual void initialize() = 0;

		virtual void process(const Context& cxt) = 0;

		virtual void finalize() {}
	};

	
}